#include <iostream>
#include "opencv2/opencv.hpp"
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <vector>

#define MAIN_WINDOW "实时人脸识别系统"
#define YUNET_ONNX_PATH "./models/yunet.onnx"
#define FACE_RECOGNIZER_FAST_ONNX_PATH "./models/face_recognizer_fast.onnx"
#define FACES_PATH "./faces/"	// 表示文件夹路径记得带上末尾/

using namespace std;
using namespace cv;

Mat faces_feature[500];
char face_name[500][100];	// 定义全局变量表示人名
int face_cnt = 0;

void face_register(void);
string face_recognize(Mat face_img, Mat face_box);
FILE* mplayer_init(void);
void mplayer_play(FILE* fp, const char* music_path);
void mplayer_pause(FILE* fp);
void mplayer_stop(FILE* fp);
void mplayer_vol(FILE*fp, int volume);


Ptr<FaceDetectorYN> fd = FaceDetectorYN::create(YUNET_ONNX_PATH, "", Size(320, 320));	// 通过人脸模型找到人脸区域
Ptr<FaceRecognizerSF> fr = FaceRecognizerSF::create(FACE_RECOGNIZER_FAST_ONNX_PATH, "");	// 加载模型，构建深度神经网络，进行人脸数据特征分析

int main()
{
	VideoCapture camera(0);		// camera()参数要参考实际的设备连接，默认是0，可以查看 /dev/video* 找到调用摄像头设备的编号
	Mat img, faces;
	int key, i, face_x, face_y, face_w, face_h;
	string name;
	int is_me = 0, music_flag = 0;
	time_t t1;
	FILE* fp = mplayer_init();
	int m_volume = 50;

	face_register();

	while(1)
	{
		if(!camera.read(img))
		{
			cerr << "摄像头打开失败" << endl;
			return 1;
		}

		fd->setInputSize(img.size());
		fd->detect(img, faces);

		is_me = 0;

		for(i = 0; i < faces.rows; i++)
		{
			name = face_recognize(img, faces.row(i));

			face_x = faces.at<float>(i, 0);
			face_y = faces.at<float>(i, 1);
			face_w = faces.at<float>(i, 2);
			face_h = faces.at<float>(i, 3);

			if(name == "me")	is_me = 1;

			rectangle(img, Rect(face_x, face_y, face_w, face_h), Scalar(200, 0, 0), 2);
		
			putText(img, name, Point(face_x, face_y - 5), FONT_HERSHEY_COMPLEX, 1.0, Scalar(200, 200, 0), 2);

		}

		// 检测人脸未出现超过5秒
		if(is_me)
		{	
			t1 = time(NULL);
		}
		if((time(NULL) - t1) > 5)
		{
			cout << "溜了溜了" << endl;
			t1 = time(NULL);
		}

		imshow(MAIN_WINDOW, img);	//显示图像 参数1：窗口名 参数2：图像对象

		key = waitKey(1);
		
		if(key == 'q')
			break;
		if(key == 'p')
		{
			if(music_flag == 0)
			{
				mplayer_play(fp, "./你瞒我瞒.mp3");
				music_flag = 1;
			}
			mplayer_pause(fp);
		}
		if(key == 's')
			mplayer_stop(fp);
		if(key == 'j')
		{
			m_volume += 10;
			if(m_volume > 100)
				m_volume = 100;
			mplayer_vol(fp, m_volume);
		}
		if(key === 'k')
		{
			m_volume -= 10;
			if(m_volume < 100)
				m_volume = 0;
			mplayer_vol(fp, m_volume);
		}
	}
	mplayer_exit(fp);
	return 0;
}

// 人脸信息注册
void face_register(void)
{
	Mat img, faces, aligned_img, face_feature;
	vector<string> path;
	const char* faces_dir = FACES_PATH;
	const char* q;
	int i;

	glob(FACES_PATH, path);	// 将文件夹中的所有文件信息读取到顺序表

	for(auto p: path)
	{
		img = imread(p);

		// 大尺寸图片缩小（等比缩放）
		if(img.cols > 1024)
		{
			resize(img, img, Size(), 1024.0/img.cols, 1024.0/img.cols);
		}

		fd->setInputSize(img.size());
		fd->detect(img, faces);
		
		if(faces.rows == 0)
		{
			cerr << p << ": 未检测到人脸" << endl;
			continue;
		}
		if(faces.rows > 1)
		{
			cerr << p << ": 检测到多个人脸信息，注册失败" << endl;
			continue;
		}

		fr->alignCrop(img, faces.row(0), aligned_img);	// 将录入的人脸图像进行对齐和裁剪，结果存放在 aligend_img 中
		fr->feature(aligned_img, face_feature);			// 计算人脸特征数据，并存放在 face_feature 中

		faces_feature[face_cnt] = face_feature.clone();	// 把人脸特征存放到全局变量中

		q = p.c_str() + strlen(faces_dir);
		
		for(i = 0; *(q + i) != '.'; i++)
			face_name[face_cnt][i] = *(q + i);
		
		face_name[face_cnt][i] = '\0';

		cout << face_name[face_cnt] << endl;
		face_cnt++;
	}

	return ;
}

// 人脸识别
string face_recognize(Mat face_img, Mat face_box)
{
	Mat aligned_img, face_feature;
	double cosine_score;
	int i;

	fr->alignCrop(face_img, face_box.row(0), aligned_img);	// 将录入的人脸图像进行对齐和裁剪，结果存放在 aligend_img 中
	fr->feature(aligned_img, face_feature);			// 计算人脸特征数据，并存放在 face_feature 中
	
	for(i = 0; i < face_cnt; i++)
	{
		cosine_score = fr->match(face_feature, faces_feature[i]); // 计算两个特征向量的相似度，采用余弦距离的方法，数值越大相似度越高

		if(cosine_score > 0.363)
		{
			return face_name[i];
		}

		return "unknown";	
	}
	return "unkown";
}

// 播放器初始化
FILE* mplayer_init(void)
{
	FILE* fp = popen("myplayer -slave -idle -really-quiet -nolirc", "w");
	return fp;
}

void mplayer_play(FILE* fp, const char* music_path)
{
	fprintf(fp, "load %s\n", music_path);
	fflush(fp);
}

void mplayer_pause(FILE* fp)
{
	static int flag = 1;
	if(flag == 1)
	{
		fprintf(fp, "p\n");
		fflush(fp);
		flag = 0;
	}
	else
	{
		fprintf(fp, "p\n");
		fflush(fp);
		flag = 1;	
	}
}

void mplayer_stop(FILE* fp)
{
	fprintf(fp, "stop\n");
	fflush(fp);
}

void mplayer_vol(FILE* fp, int volume)
{
	fprintf(fp, "volume %d\n", volume);
	fflush(fp);
}

void mplayer_exit(FILE* fp)
{
	fprintf(fp, "q\n");
	fflush(fp);	
	pclose(fp);
}